package com.xqboss.apps.service.user;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.constant.LockKey;
import com.xqboss.apps.domain.user.SysUserWallet;
import com.xqboss.apps.domain.user.SysUserWalletLog;
import com.xqboss.apps.domain.user.UserAssetLidou;
import com.xqboss.apps.domain.user.UserAssetWrench;
import com.xqboss.apps.dto.user.ChangeUserWalletDto;
import com.xqboss.apps.enums.user.UserWalletEnum;
import com.xqboss.apps.enums.user.WalletChangeDirectionEnum;
import com.xqboss.apps.enums.user.WalletChangeTypeEnum;
import com.xqboss.apps.enums.user.WalletWrenchChangeTypeEnum;
import com.xqboss.apps.mapper.user.SysUserWalletMapper;
import com.xqboss.common.constant.HttpStatus;
import com.xqboss.common.core.domain.R;
import com.xqboss.common.core.redis.lock.LockValue;
import com.xqboss.common.core.redis.lock.RedisLock;
import com.xqboss.common.exception.ServiceException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * @author Trent
 * @date 2023/8/14
 */
@Service
public class SysUserWalletService extends ServiceImpl<SysUserWalletMapper, SysUserWallet> {

    @Autowired
    private SysUserWalletLogService sysUserWalletLogService;

    @Autowired
    private SysUserWalletRecordService sysUserWalletRecordService;
    @Autowired
    private UserAssetWrenchService userAssetWrenchService;
    @Autowired
    private UserAssetWrenchRecordService userAssetWrenchRecordService;
    @Autowired
    private UserAssetLidouRecordService userAssetLidouRecordService;

    @Autowired
    private UserAssetLidouService userAssetLidouService;

    /**
     * 初始化用户钱包
     */
    @Transactional(rollbackFor = Exception.class)
    public void initWallet(Long userId) {
        initWallet(userId, UserWalletEnum.CASH);
        initWallet(userId, UserWalletEnum.INTEGRAL);
    }

    private void initWallet(Long userId, UserWalletEnum type) {
        SysUserWallet wallet = new SysUserWallet();
        wallet.setUserId(userId);
        wallet.setType(type);
        save(wallet);
    }

    public SysUserWallet getWallet(Long userId, UserWalletEnum walletType) {
        if (walletType == UserWalletEnum.INTEGRAL || walletType == UserWalletEnum.CASH){
            // 热豆或者平台或者力豆钱包
            SysUserWallet wallet = getOne(new LambdaUpdateWrapper<SysUserWallet>()
                    .eq(SysUserWallet::getUserId, userId)
                    .eq(SysUserWallet::getType, walletType));
            if (wallet == null) {
                //初始化钱包
                initWallet(userId, walletType);
                wallet = getOne(new LambdaUpdateWrapper<SysUserWallet>()
                        .eq(SysUserWallet::getUserId, userId)
                        .eq(SysUserWallet::getType, walletType));
            }
            return wallet;
        } else if (walletType == UserWalletEnum.WRENCH) {
            // 扳手
            SysUserWallet wallet = new SysUserWallet();
            UserAssetWrench wrench = userAssetWrenchService.getByUserId(userId);
            if (wrench != null){
                BeanUtils.copyProperties(wrench,wallet);
            }
            return wallet;
        } else if (walletType == UserWalletEnum.LIDOU) {
            // 力豆
            SysUserWallet wallet = new SysUserWallet();
            UserAssetLidou lidou = userAssetLidouService.getLidouInfo(userId);
            if (lidou != null){
                BeanUtils.copyProperties(lidou,wallet);
            }
            return wallet;
        }

        throw new ServiceException("暂无这个类型");
    }

    /**
     * 增加钱包资产
     *
     * @param userId     用户ID
     * @param walletType 钱包类型
     * @param amount     变动金额
     * @param remarks    备注
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_WALLET)
    @Transactional(rollbackFor = Exception.class)
    public void increase(@LockValue Long userId, UserWalletEnum walletType, BigDecimal amount, WalletChangeTypeEnum changeType, Long sourceId, String remarks) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException();
        }
        SysUserWallet wallet = getWallet(userId, walletType);
        BigDecimal newAmount = wallet.getAmount().add(amount);
        LambdaUpdateWrapper<SysUserWallet> updateWrapper = new LambdaUpdateWrapper<SysUserWallet>()
                .setSql("amount=amount+" + amount)
                .eq(SysUserWallet::getId, wallet.getId());
        if (changeType == WalletChangeTypeEnum.RECHARGE) {
            // 用户充值，增加累计充值
            updateWrapper.setSql("total_recharge=total_recharge+" + amount);
        }
        if (changeType == WalletChangeTypeEnum.MARKET_INCOME){
            // 市场收入，累加到优惠额度
            updateWrapper.setSql("free_withdraw_amount = free_withdraw_amount + " + amount);
        }
        boolean update = update(updateWrapper);
        if (!update) {
            throw new ServiceException("钱包操作失败");
        }

        // 记录日志
        record(walletType, amount, changeType, sourceId, remarks, wallet, newAmount,WalletChangeDirectionEnum.IN);
    }

    /**
     * 扣除资产
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_WALLET)
    @Transactional(rollbackFor = Exception.class)
    public void reduce(@LockValue Long userId, UserWalletEnum walletType, BigDecimal amount, WalletChangeTypeEnum changeType, Long sourceId, String remarks) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException();
        }
        //如果amount等于0，直接返回
        if (amount.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        SysUserWallet wallet = getWallet(userId, walletType);
        if (wallet.getAmount().compareTo(amount) < 0) {
            switch (walletType){
                case INTEGRAL:
                    throw new ServiceException("热豆不够啦~", HttpStatus.Insufficient_Balance);
                case CASH:
                    throw new ServiceException("余额不足", HttpStatus.Insufficient_Balance);
            }
            throw new ServiceException("余额不足", HttpStatus.Insufficient_Balance);
        }

        BigDecimal newAmount = wallet.getAmount().subtract(amount);
        LambdaUpdateWrapper<SysUserWallet> updateWrapper = new LambdaUpdateWrapper<SysUserWallet>()
                .setSql("amount=amount-" + amount)
                .eq(SysUserWallet::getId, wallet.getId())
                .ge(SysUserWallet::getAmount, amount);
        if (changeType == WalletChangeTypeEnum.ORDER_PAY
                || changeType == WalletChangeTypeEnum.INTERACT
                || changeType == WalletChangeTypeEnum.REPAIRS
                || changeType == WalletChangeTypeEnum.WARN_PUNCH) {
            updateWrapper.setSql("total_expend=total_expend+" + amount);
        }
        boolean update = update(updateWrapper);
        if (!update) {
            throw new ServiceException("钱包操作失败");
        }

        // 记录日志
        record(walletType, amount, changeType, sourceId, remarks, wallet, newAmount,WalletChangeDirectionEnum.OUT);
    }

    /**
     * 冻结资产
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_WALLET)
    @Transactional(rollbackFor = Exception.class)
    public void freeze(@LockValue Long userId, UserWalletEnum walletType, BigDecimal amount, WalletChangeTypeEnum changeType, Long sourceId, String remarks) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException();
        }
        SysUserWallet wallet = getWallet(userId, walletType);
        if (wallet.getAmount().compareTo(amount) < 0) {
            throw new ServiceException("余额不足");
        }

        BigDecimal newAmount = wallet.getAmount().subtract(amount);
        BigDecimal newFreeze = wallet.getFreeze().add(amount);
        boolean update = update(new LambdaUpdateWrapper<SysUserWallet>()
                .setSql("amount=amount-" + amount)
                .setSql("freeze=freeze+" + amount)
                .eq(SysUserWallet::getId, wallet.getId()));
        if (!update) {
            throw new ServiceException("钱包操作失败");
        }

        // 记录日志
        record(walletType, amount, changeType, sourceId, remarks, wallet, newAmount,WalletChangeDirectionEnum.OUT);
    }

    /**
     * 解冻资产
     *
     * @param userId
     * @param walletType
     * @param amount
     * @param remarks
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_WALLET)
    @Transactional(rollbackFor = Exception.class)
    public void unfreeze(@LockValue Long userId, UserWalletEnum walletType, BigDecimal amount, WalletChangeTypeEnum changeType, Long sourceId, String remarks) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException();
        }
        SysUserWallet wallet = getWallet(userId, walletType);
        if (wallet.getFreeze().compareTo(amount) < 0) {
            throw new ServiceException("余额不足");
        }

        BigDecimal newAmount = wallet.getAmount().add(amount);
        BigDecimal newFreeze = wallet.getFreeze().subtract(amount);
        boolean update = update(new LambdaUpdateWrapper<SysUserWallet>()
                .setSql("amount=amount+" + amount)
                .setSql("freeze=freeze-" + amount)
                .eq(SysUserWallet::getId, wallet.getId()));
        if (!update) {
            throw new ServiceException("钱包操作失败");
        }

        // 记录日志
        record(walletType, amount, changeType, sourceId, remarks, wallet, newAmount,WalletChangeDirectionEnum.IN);
    }

    /**
     * 提现冻结
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_WALLET)
    @Transactional(rollbackFor = Exception.class)
    public void withdrawFreeze(@LockValue Long userId, UserWalletEnum walletType, BigDecimal amount, BigDecimal useFreeWithdrawAmount, WalletChangeTypeEnum changeType, Long sourceId, String remarks) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException();
        }
        SysUserWallet wallet = getWallet(userId, walletType);
        if (wallet.getAmount().compareTo(amount) < 0) {
            throw new ServiceException("余额不足");
        }

        BigDecimal newAmount = wallet.getAmount().subtract(amount);
        BigDecimal newWithdraw = wallet.getWithdraw().add(amount);
        boolean update = update(new LambdaUpdateWrapper<SysUserWallet>()
                .setSql("amount=amount-" + amount)
                .setSql("withdraw=withdraw+" + amount)
                .setSql("free_withdraw_amount=free_withdraw_amount-" + useFreeWithdrawAmount)
                .eq(SysUserWallet::getId, wallet.getId()));
        if (!update) {
            throw new ServiceException("钱包操作失败");
        }

        // 记录日志
        record(walletType, amount, changeType, sourceId, remarks, wallet, newAmount,WalletChangeDirectionEnum.OUT);
    }

    /**
     * 提现完成
     *
     * @param userId
     * @param walletType
     * @param amount
     * @param remarks
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_WALLET)
    @Transactional(rollbackFor = Exception.class)
    public void withdrawFinish(@LockValue Long userId, UserWalletEnum walletType, BigDecimal amount, String remarks) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException();
        }
        SysUserWallet wallet = getWallet(userId, walletType);
        if (wallet.getWithdraw().compareTo(amount) < 0) {
            throw new ServiceException("余额不足");
        }

        BigDecimal newWithdraw = wallet.getWithdraw().subtract(amount);
        boolean update = update(new LambdaUpdateWrapper<SysUserWallet>()
                .setSql("withdraw=withdraw-" + amount)
                .setSql("total_withdraw=total_withdraw+" + amount)
                .eq(SysUserWallet::getId, wallet.getId()));
        if (!update) {
            throw new ServiceException("钱包操作失败");
        }
        // 记录日志
        recordLog(wallet, wallet.getAmount(), wallet.getFreeze(), newWithdraw, remarks);
    }

    /**
     * 提现驳回
     *
     * @param userId
     * @param walletType
     * @param amount
     * @param remarks
     */
    @RedisLock(lockKey = LockKey.LOCK_USER_WALLET)
    @Transactional(rollbackFor = Exception.class)
    public void withdrawAbort(@LockValue Long userId, UserWalletEnum walletType, BigDecimal amount, BigDecimal useFreeWithdrawAmount, WalletChangeTypeEnum changeType, Long sourceId, String remarks) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException();
        }
        SysUserWallet wallet = getWallet(userId, walletType);
        if (wallet.getWithdraw().compareTo(amount) < 0) {
            throw new ServiceException("余额不足");
        }

        BigDecimal newWithdraw = wallet.getWithdraw().subtract(amount);
        BigDecimal newAmount = wallet.getAmount().add(amount);
        boolean update = update(new LambdaUpdateWrapper<SysUserWallet>()
                .setSql("amount=amount+" + amount)
                .setSql("withdraw=withdraw-" + amount)
                .setSql("free_withdraw_amount=free_withdraw_amount+" + useFreeWithdrawAmount)
                .eq(SysUserWallet::getId, wallet.getId())
                .ge(SysUserWallet::getWithdraw, amount));
        if (!update) {
            throw new ServiceException("钱包操作失败");
        }

        // 记录日志
        record(walletType, amount, changeType, sourceId, remarks, wallet, newAmount,WalletChangeDirectionEnum.IN);
    }


    /**
     * 记录钱包流水记录
     *
     * @param walletType
     * @param amount
     * @param changeType
     * @param sourceId
     * @param remarks
     * @param wallet
     * @param newAmount
     * @param direction 变动方向（入，出）
     */
    private void record(UserWalletEnum walletType, BigDecimal amount, WalletChangeTypeEnum changeType, Long sourceId, String remarks, SysUserWallet wallet, BigDecimal newAmount,WalletChangeDirectionEnum direction) {
        if (walletType == UserWalletEnum.INTEGRAL || walletType == UserWalletEnum.CASH ){
            sysUserWalletRecordService.addRecord(wallet, changeType, direction, amount, newAmount, sourceId, remarks);
            // 记录日志
            recordLog(wallet, newAmount, wallet.getFreeze(), wallet.getWithdraw(), remarks);
        }
    }

    /**
     * 记录钱包变动日志
     *
     * @param oldWallet
     * @param newAmount
     * @param newFreeze
     * @param newWithdraw
     * @param remarks
     */
    private void recordLog(SysUserWallet oldWallet, BigDecimal newAmount, BigDecimal newFreeze, BigDecimal newWithdraw, String remarks) {
        SysUserWalletLog walletLog = new SysUserWalletLog();
        walletLog.setUserId(oldWallet.getUserId());
        walletLog.setWalletId(oldWallet.getId());
        walletLog.setType(oldWallet.getType());
        walletLog.setOldAmount(oldWallet.getAmount());
        walletLog.setOldFreeze(oldWallet.getFreeze());
        walletLog.setOldWithdraw(oldWallet.getWithdraw());
        walletLog.setNewAmount(newAmount);
        walletLog.setNewFreeze(newFreeze);
        walletLog.setNewWithdraw(newWithdraw);
        walletLog.setRemark(remarks);
        sysUserWalletLogService.syncRecordLog(walletLog);
    }

    /**
     * 统一操作用户资产
     * @param params
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public R handleWalletChange(ChangeUserWalletDto params) {
        if (params.getType() == UserWalletEnum.WRENCH) {
            userAssetWrenchRecordService.updateWrenchBalance(
                    params.getUserId(),
                    WalletWrenchChangeTypeEnum.ADMIN,
                    params.getDirection(),params.getAmount(),
                    null,
                    params.getRemarks());
            return R.ok();
        }
        // 用户钱包详情
        if (params.getDirection() == WalletChangeDirectionEnum.IN) {
            this.increase(params.getUserId(), params.getType(), params.getAmount(), WalletChangeTypeEnum.ADMIN, null, params.getRemarks());
        } else {
            this.reduce(params.getUserId(), params.getType(), params.getAmount(), WalletChangeTypeEnum.ADMIN, null, params.getRemarks());
        }
        return R.ok();
    }

}
